package javax.core.common.tree;

import org.springframework.beans.BeanUtils;
import org.springframework.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 树形结构工具类， 对树形结构抽象 {@link TreeNode} 进行的各种操作操作
 */
public class TreeNodeUtil {

    public static <T> List<TreeNode<T>> createTreeNodeList(Class<T> clazz) {
        return new ArrayList<>();
    }

    public static <V> void appendRootNode(List<TreeNode<V>> rootNodeList, V entity) {
        TreeNode<V> treeNode = new TreeNode<V>(true, 0, entity, null);
        if (Objects.isNull(rootNodeList)) {
            throw new IllegalArgumentException("root node list cannot be null(父级节点列表不能为空)");
        }
        if (rootNodeList.size() > 0) {
            TreeNode<V> lastNode = rootNodeList.get(rootNodeList.size() - 1);
            lastNode.setNextNode(treeNode);
            treeNode.setPreviousNode(lastNode);
        }
        rootNodeList.add(treeNode);
    }

    /**
     * 遍历一个树结构，把一个对象 entity 添加到，某个TreeNode下，该TreeNode满足
     * @param rootNodeList
     * @param entity
     * @param idField
     * @param parentIdField
     * @param <V>
     */
    public static <V> void appendSubNode(List<TreeNode<V>> rootNodeList, V entity, String idField, String parentIdField) {
        if (Objects.isNull(rootNodeList) || rootNodeList.size() == 0) {
            return;
        }
        // 通过反射得到parentId的值
        Object parentIdValue = getBeanProperty(entity, parentIdField);
        //遍历Root TreeNode List 来寻找父级TreeNode
        for (TreeNode<V> subNode : rootNodeList) {
            //遍历单个treeNode来寻找父级TreeNode
            TreeNode<V> parentNode = findNodeByParentId(subNode, parentIdValue, idField, parentIdField);
            if (Objects.nonNull(parentNode)) {
                // 找到父node
                // 找平级的上一个node，即父node的子列表node中最后一个
                TreeNode<V> previousNode = getLastSubNode(parentNode);
                TreeNode<V> treeNode = new TreeNode<V>(false, parentNode.getDepth() + 1, entity, parentNode, previousNode);
                addSubNode(parentNode, treeNode);
                if (previousNode != null) {
                    previousNode.setNextNode(treeNode);
                    treeNode.setPreviousNode(previousNode);
                }
            }
        }
    }

    private static <V> void addSubNode(TreeNode<V> parentNode, TreeNode<V> subNode) {
        List<TreeNode<V>> subNodeList = parentNode.getSubNodeList();
        if (subNodeList == null) {
            subNodeList = new ArrayList<>();
        }
        subNodeList.add(subNode);
    }

    private static <V> TreeNode<V> getLastSubNode(TreeNode<V> parentNode) {
        if (parentNode != null && parentNode.getSubNodeList() != null && parentNode.getSubNodeList().size() > 0) {
            return parentNode.getSubNodeList().get(parentNode.getSubNodeList().size() - 1);
        }
        return null;
    }

    public static <V> TreeNode<V> findNodeByParentId(TreeNode<V> treeNode, Object parentId, String idField, String parentIdField) {
        Object idValue = getBeanProperty(treeNode.getData(), idField);
        if (idValue != null && idValue.equals(parentId)) {
            return treeNode;
        }
        List<TreeNode<V>> subTreeNodeList = treeNode.getSubNodeList();
        if (subTreeNodeList != null && !subTreeNodeList.isEmpty()) {
            for (TreeNode<V> subNode : subTreeNodeList) {
                TreeNode<V> node = findNodeByParentId(subNode, parentId, idField, parentIdField);
                if (Objects.nonNull(node)) {
                    return node;
                }
            }
        }
        return null;
    }

    private static <V> Object getBeanProperty(V entity, String propertyName) {
        PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(entity.getClass(), propertyName);
        if (Objects.isNull(propertyDescriptor) || Objects.isNull(propertyDescriptor.getReadMethod())) {
            return null;
        }
        return ReflectionUtils.invokeMethod(propertyDescriptor.getReadMethod(), entity);
    }
}
